/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
        http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

#include "common/type/date_type.h"
#include "common/lang/comparator.h"
#include "common/lang/sstream.h"
#include "common/lang/string.h"
#include "common/log/log.h"
#include "common/value.h"
#include "storage/common/column.h"

// 参考 Howard Hinnant 的 days_from_civil / civil_from_days 算法，支持公元范围内的负数年

bool DateType::is_leap(int y)
{
  return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}

bool DateType::valid_ymd(int y, int m, int d)
{
  if (m < 1 || m > 12) return false;
  static const int mdays[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
  int days = mdays[m-1] + (m == 2 && is_leap(y) ? 1 : 0);
  return d >= 1 && d <= days;
}

// 将 civil 日期转换为相对 1970-01-01 的日序(可负)
int DateType::days_from_civil(int y, unsigned m, unsigned d)
{
  y -= m <= 2;
  const int era = (y >= 0 ? y : y-399) / 400;
  const unsigned yoe = static_cast<unsigned>(y - era*400);      // [0, 399]
  const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
  const unsigned doe = yoe*365 + yoe/4 - yoe/100 + doy;          // [0, 146096]
  // 将基准改为 1970-01-01
  // days_from_civil(1970,1,1) == 0
  // 先得到绝对日序(相对 0000-03-01)
  int z = era*146097 + static_cast<int>(doe) - 719468; // 719468 是 0000-03-01 到 1970-01-01 的天数
  return z;
}

void DateType::civil_from_days(int z, int &y, unsigned &m, unsigned &d)
{
  z += 719468;
  const int era = (z >= 0 ? z : z-146096) / 146097;
  const unsigned doe = static_cast<unsigned>(z - era*146097);          // [0, 146096]
  const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
  y = static_cast<int>(yoe) + era*400;
  const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100);
  const unsigned mp = (5*doy + 2)/153;                                 // [0, 11]
  d = doy - (153*mp + 2)/5 + 1;                                        // [1, 31]
  m = mp + (mp < 10 ? 3 : -9);                                         // [1, 12]
  y += (m <= 2);
}

bool DateType::parse_date(const string &s, int &y, int &m, int &d)
{
  // 允许 yyyy-mm-dd，年份可为负；严格校验
  // 去空白
  string t = s;
  common::strip(t);
  if (t.size() < 8) return false;
  // 手动解析，避免 strptime 依赖
  int sign = 1;
  size_t i = 0;
  if (t[i] == '+') { sign = 1; ++i; }
  else if (t[i] == '-') { sign = -1; ++i; }
  // year
  long long year = 0;
  bool has_y = false;
  while (i < t.size() && isdigit(static_cast<unsigned char>(t[i]))) {
    has_y = true;
    year = year*10 + (t[i]-'0');
    ++i;
  }
  if (!has_y) return false;
  if (i >= t.size() || t[i] != '-') return false;
  ++i;
  // month: 支持1或2位数字
  int m_len = 0;
  m = 0;
  while (m_len < 2 && i < t.size() && isdigit(static_cast<unsigned char>(t[i]))) {
    m = m * 10 + (t[i] - '0');
    ++i;
    ++m_len;
  }
  if (m_len == 0) return false;
  if (i >= t.size() || t[i] != '-') return false;
  ++i;
  // day: 支持1或2位数字
  int d_len = 0;
  d = 0;
  while (d_len < 2 && i < t.size() && isdigit(static_cast<unsigned char>(t[i]))) {
    d = d * 10 + (t[i] - '0');
    ++i;
    ++d_len;
  }
  if (d_len == 0) return false;
  if (i != t.size()) return false;
  y = static_cast<int>(sign * year);
  return valid_ymd(y, m, d);
}

int DateType::compare(const Value &left, const Value &right) const
{
  ASSERT(left.attr_type() == AttrType::DATES, "left type is not date");
  ASSERT(right.attr_type() == AttrType::DATES, "right type is not date");
  return common::compare_int((void *)&left.value_.int_value_, (void *)&right.value_.int_value_);
}

int DateType::compare(const Column &left, const Column &right, int left_idx, int right_idx) const
{
  ASSERT(left.attr_type() == AttrType::DATES, "left type is not date");
  ASSERT(right.attr_type() == AttrType::DATES, "right type is not date");
  return common::compare_int((void *)&((int*)left.data())[left_idx], (void *)&((int*)right.data())[right_idx]);
}

int DateType::cast_cost(AttrType type)
{
  if (type == AttrType::DATES) return 0;
  if (type == AttrType::CHARS) return 2; 
  return INT32_MAX;
}

RC DateType::cast_to(const Value &val, AttrType type, Value &result) const
{
  switch (type) {
    case AttrType::DATES: {
      result = val;
      return RC::SUCCESS;
    }
    case AttrType::CHARS: {
      string s;
      RC rc = to_string(val, s);
      if (rc != RC::SUCCESS) return rc;
      result.set_string(s.c_str());
      return RC::SUCCESS;
    }
    default:
      LOG_WARN("unsupported cast from date to %d", type);
      return RC::SCHEMA_FIELD_TYPE_MISMATCH;
  }
}

RC DateType::set_value_from_str(Value &val, const string &data) const
{
  int y, m, d;
  if (!parse_date(data, y, m, d)) {
    return RC::SCHEMA_FIELD_TYPE_MISMATCH; // 非法日期 -> 失败
  }
  int days = days_from_civil(y, static_cast<unsigned>(m), static_cast<unsigned>(d));
  val.set_type(AttrType::DATES);
  val.set_data((char *)&days, sizeof(int));
  return RC::SUCCESS;
}

RC DateType::to_string(const Value &val, string &result) const
{
  int days = val.value_.int_value_;
  int y; unsigned m; unsigned d;
  civil_from_days(days, y, m, d);
  char buf[32];
  snprintf(buf, sizeof(buf), "%04d-%02u-%02u", y, m, d);
  result = buf;
  return RC::SUCCESS;
}


